#pragma once
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <unordered_map>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include "TcpServer.hpp"
#include "Util.hpp"

#define HEADER_SEP ": "
#define LINE_END "\r\n"
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

#define HTTP_VERSION "HTTP/1.0"
#define PAGE_404 "404.html"

class HttpRequest // http请求
{
public:
    std::string _request_line;                // 请求行
    std::vector<std::string> _request_header; // 请求报头
    std::string _blank;                       // 空行
    std::string _request_body;                // 请求正文

    // 解析完毕之后的请求行
    std::string _method;
    std::string _uri;
    std::string _version;

    // 解析完毕之后的请求报头
    std::unordered_map<std::string, std::string> _header_kv;

    int _content_length = -1; // 请求正文长度
    bool _cgi = false;        // 是否启用cgi
    int _size = -1;           // 请求资源大小

    std::string _path;         // 请求资源路径
    std::string _suffix;       // 请求资源后缀
    std::string _query_string; // 参数
};

class HttpResponse
{

public:
    HttpResponse()
        : _blank(LINE_END), _status_code(OK), _fd(-1)
    {
    }
    ~HttpResponse() {}

    std::string _status_line;                  // 状态行
    std::vector<std::string> _response_header; // 响应报头
    std::string _blank;                        // 空行
    std::string _response_body;                // 响应正文

    int _status_code = -1; // 响应状态码
    int _fd = -1;          // 资源fd
};

class EndPoint // 读取请求，分析请求，构建响应, IO通信
{
public:
    EndPoint(int sock)
        : _sock(sock), _stop(false)
    {
    }
    ~EndPoint()
    {
        if (_sock > 0)
        {
            close(_sock);
        }
    }
    void handle()
    {
        RecvHttpRequest();
        BuildHttpResponse();
        SendHttpResponse();
    }

private:
    void RecvHttpRequest()
    {
        if ((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()))
        {
            ParseHttpRequestLine();
            ParseHttpRequestHeader();
            if (IsNeedRecvHttpRequestBody())
            {
                RecvHttpRequestBody();
            }
        }
    }
    void BuildHttpResponse()
    {
        // 请求已经全部读完,可以构建响应
        if (ResolvingHandler()) // 解析资源路径,明确所需资源
        {
            ResourceHandler(); // 处理资源
        }
        BuildHttpResponseHelper(); // 构建响应报头、正文
    }

    bool RecvHttpRequestLine()
    {
        auto &line = _http_request._request_line;
        int ret_code = Util::ReadLine(_sock, line);
        if (ret_code > 0)
        {
            line.resize(line.size() - 1); // 舍弃'\n'
            DLOG("RecvHttpRequestLine: %s", line.c_str());
        }
        else
        {
            ILOG("RecvHttpRequestLine failed, ret_code: %d", ret_code);
            _stop = true;
        }
        return _stop;
    }
    void ParseHttpRequestLine()
    {
        auto &line = _http_request._request_line;
        std::stringstream ss(line);
        ss >> _http_request._method >> _http_request._uri >> _http_request._version;
        auto &method = _http_request._method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper); // 方法标准为大写

        auto &uri = _http_request._uri;
        const std::string prefix = "http://";
        if (uri.find(prefix) == 0) // 删除http://
        {
            uri.erase(0, prefix.length());
        }
        DLOG("method:%s", _http_request._method.c_str());
        DLOG("uri:%s", _http_request._uri.c_str());
        DLOG("version:%s", _http_request._version.c_str());
    }
    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (1)
        {
            line.clear();
            if (Util::ReadLine(_sock, line) <= 0)
            {
                _stop = true;
                break;
            }
            if (line == "\n")
            {
                _http_request._blank = line;
                break;
            }
            line.resize(line.size() - 1);
            _http_request._request_header.push_back(line);
            ILOG("%s", line.c_str());
        }
        return _stop;
    }
    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        for (auto &it : _http_request._request_header)
        {
            key.clear();
            value.clear();
            if (Util::CutString(it, key, value, HEADER_SEP))
            {
                _http_request._header_kv.insert({key, value});
                DLOG("request_header: %s : %s", key.c_str(), value.c_str());
            }
        }
    }

    bool IsNeedRecvHttpRequestBody()
    {
        auto &method = _http_request._method;
        if (method == "POST")
        {
            auto &header_kv = _http_request._header_kv;
            auto iter = header_kv.find("Content-Length");
            if (iter != header_kv.end())
            {
                ILOG("!!!!!Post Method, Content-Length: %d", std::atoi(iter->second.c_str()));
                _http_request._content_length = std::atoi(iter->second.c_str());
                
                return true;
            }
        }
        return false;
    }
    void RecvHttpRequestBody()
    {
        int content_length = _http_request._content_length;
        auto &body = _http_request._request_body;
        char ch = 0;
        while (content_length--)
        {
            ssize_t s = recv(_sock, &ch, 1, 0);
            if (s > 0)
            {
                body.push_back(ch);
            }
            else
            {
                DLOG("Request Body:%s", body.c_str());
                break;
            }
        }
        DLOG("Request Body:%s", body.c_str());
    }

    bool ResolvingHandler()
    {
        std::string path; // 请求文件路径
        struct stat st;   // 文件信息
        size_t found;
        auto &code = _http_response._status_code;

        if (_http_request._method != "GET" && _http_request._method != "POST")
        {
            // 非法请求
            ILOG("method illegal:%s", _http_request._method.c_str());
            code = BAD_REQUEST;
            return false;
        }
        if (_http_request._method == "GET")
        {
            size_t pos = _http_request._uri.find('?'); // 有?则需cgi机制
            if (pos != std::string::npos)
            {
                Util::CutString(_http_request._uri, _http_request._path, _http_request._query_string, "?"); // 分离路径和参数
                _http_request._cgi = true;
            }
            else
            {
                _http_request._path = _http_request._uri;
            }
        }
        else if (_http_request._method == "POST")
        {
            // POST默认启用cgi
            _http_request._cgi = true;
            _http_request._path = _http_request._uri;
        }
        else
        {
            // Do Nothing
        }
        path = _http_request._path;
        _http_request._path = WEB_ROOT; // 添加wwwroot
        _http_request._path += path;

        if (_http_request._path[_http_request._path.size() - 1] == '/')
        {
            _http_request._path += HOME_PAGE; // 添加wwwroot目录主页
        }
        if (stat(_http_request._path.c_str(), &st) == 0)
        {
            // 说明资源是存在的
            if (S_ISDIR(st.st_mode))
            {
                // 请求的资源是一个目录，需添加该目录主页
                _http_request._path += "/";
                _http_request._path += HOME_PAGE;
                stat(_http_request._path.c_str(), &st);
            }
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 请求可执行程序
                _http_request._cgi = true;
            }
            _http_request._size = st.st_size;
        }
        else
        {
            // 请求资源不存在
            ILOG("%s not found", _http_request._path.c_str());
            code = NOT_FOUND;
            return false;
        }
        found = _http_request._path.rfind("."); // 获取资源后缀
        if (found == std::string::npos)
        {
            _http_request._suffix = ".html";
        }
        else
        {
            _http_request._suffix = _http_request._path.substr(found);
        }
        return true;
    }

    void ResourceHandler()
    {
        int &code = _http_response._status_code; // 更新响应状态码
        if (_http_request._cgi)
        {
            code = ProcessCgi(); // 执行目标程序，拿到结果存入_http_response._response_body;
        }
        else
        {
            code = ProcessNonCgi(); // 简单的网页返回，返回静态网页
        }
    }

    int ProcessNonCgi()
    {
        _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
        if (_http_response._fd >= 0)
        {
            ILOG("%s open success!", _http_request._path.c_str());
            return OK;
        }
        return NOT_FOUND;
    }
    int ProcessCgi() // 创建子进程进程切换执行，父子由匿名管道通信
    {
        ILOG("process cgi mthod!");
        int code = OK;
        // 父进程数据
        auto &method = _http_request._method;
        auto &query_string = _http_request._query_string; // GET方法，参数
        auto &body_text = _http_request._request_body;    // POST方法，正文
        auto &bin = _http_request._path;                  // 子进程执行的目标程序路径
        int content_length = _http_request._content_length;
        auto &response_body = _http_response._response_body;
        // 需导入的环境变量
        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        int input[2]; // 父进程视角，规定下标0为读，1为写
        int output[2];
        if (pipe(input) < 0)
        {
            ELOG("pipe input error!");
            code = SERVER_ERROR;
            return code;
        }
        if (pipe(output) < 0)
        {
            ELOG("pipe output error!");
            code = SERVER_ERROR;
            return code;
        }

        pid_t pid = fork();
        if (pid == 0) // child
        {
            close(input[0]);
            close(output[1]);

            method_env = "METHOD=";
            method_env += method;

            putenv((char *)method_env.c_str());

            if (method == "GET") // 导入请求参数
            {
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char *)query_string_env.c_str());
                ILOG("GET Method, Add Query_Stirng_Env");
            }
            else if (method == "POST") // 导入请求正文长度，正文内容由管道传输
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char *)content_length_env.c_str());
                ILOG("Post Method, Add Content_Length Env");
            }
            else
            {
                // for Syntax checking
            }

            ILOG("child process ready execl, path:%s", bin.c_str());

            dup2(output[0], 0); // 对于子进程，0为管道读，1为管道写
            dup2(input[1], 1);

            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (pid < 0) // error
        {
            ELOG("fork error!");
            return 404;
        }
        else // parent
        {
            close(input[1]);
            close(output[0]);

            if (method == "POST") // 把请求正文通过管道发给子进程
            {
                DLOG("正文：%s", body_text.c_str());
                const char *start = body_text.c_str();
                int total = 0;
                int size = 0;
                while (total < content_length && (size = write(output[1], start + total, body_text.size() - total)) > 0)
                {
                    total += size;
                } // 管道中写入有可能被中断，宜采用while确保完全写入
            }
            char ch = 0; // 获取子进程处理结果
            while (read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }

            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
                if (WIFEXITED(status)) // 获取子进程退出状态
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }
        return code;
    }
    void BuildHttpResponseHelper()
    {
        auto &code = _http_response._status_code;
        auto &status_line = _http_response._status_line; // 构建状态行
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;
        // 构建响应正文,可能包括响应报头
        std::string path = WEB_ROOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
        case BAD_REQUEST:
            path += PAGE_404;
            HandlerError(path);
            break;
        case SERVER_ERROR:
            path += PAGE_404;
            HandlerError(path);
            break;
        default:
            break;
        }
    }
    static std::string Code2Desc(int code)
    {
        std::string desc;
        switch (code)
        {
        case 200:
            desc = "OK";
            break;
        case 404:
            desc = "Not Found";
            break;
        default:
            break;
        }
        return desc;
    }
    void BuildOkResponse() // 填充响应报头
    {
        std::string line = "Content-Type: ";
        line += Suffix2Desc(_http_request._suffix);
        line += LINE_END;
        _http_response._response_header.push_back(line);

        line = "Content-Length: ";
        if (_http_request._cgi)
        {
            line += std::to_string(_http_response._response_body.size());
        }
        else
        {
            line += std::to_string(_http_request._size);
        }
        line += LINE_END;
        _http_response._response_header.push_back(line);
    }
    static std::string Suffix2Desc(const std::string &suffix)
    {
        static std::unordered_map<std::string, std::string> suffix2desc =
            {
                {".html", "text/html"},
                {".css", "text/css"},
                {".js", "application/javascript"},
                {".jpg", "application/x-jpg"},
                {".xml", "application/xml"},
            };

        auto iter = suffix2desc.find(suffix);
        if (iter != suffix2desc.end())
        {
            return iter->second;
        }
        return "text/html";
    }
    void HandlerError(std::string page)
    {
        _http_request._cgi = false;
        // 返回404页面
        _http_response._fd = open(page.c_str(), O_RDONLY);
        if (_http_response._fd > 0)
        {
            struct stat st;
            stat(page.c_str(), &st);
            _http_request._size = st.st_size;

            std::string line = "Content-Type: text/html";
            line += LINE_END;
            _http_response._response_header.push_back(line);

            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _http_response._response_header.push_back(line);
        }
    }
    void SendHttpResponse()
    {
        send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
        for (auto iter : _http_response._response_header)
        {
            send(_sock, iter.c_str(), iter.size(), 0);
        }
        send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
        if (_http_request._cgi)
        {
            auto &response_body = _http_response._response_body;
            size_t size = 0;
            size_t total = 0;
            const char *start = response_body.c_str();
            while (total < response_body.size() && (size = send(_sock, start + total, response_body.size() - total, 0)) > 0)
            {
                total += size;
            }
            DLOG("cgi already send %d", (int)total);
        }
        else
        {
            sendfile(_sock, _http_response._fd, nullptr, _http_request._size); // 直接将对应的文件发给客户端
            close(_http_response._fd);
        }
    }

private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;
    bool _stop;
};

class CallBack
{
public:
    CallBack()
    {
    }
    void operator()(int sock)
    {
        HandlerRequest(sock);
    }
    void HandlerRequest(int sock)
    {
        ILOG("Hander Request Begin");
        EndPoint *ep = new EndPoint(sock);
        ep->handle();
        delete ep;
        ILOG("Hander Request End");
    }
    ~CallBack()
    {
    }
};
